{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _nb_algorithms_hyperparameters:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Hyperparameters"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. admonition:: Info\n",
    "    :class: myOwnStyle\n",
    "    \n",
    "    Hyperparameter optimization is a new feature available since version **0.6.0**. In general, this is quite a challenging and computationally expensive topic, and only a few basics are presented in this guide. If you are interested in contributing or collaborating, please let us know to enrich this module with more robust and better features."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Most algoriths have **hyperparameters**. For some optimization methods the parameters are already defined and can directly be optimized. For instance, for Differential Evolution (DE) the parameters can be found by:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "from pymoo.algorithms.soo.nonconvex.de import DE\n",
    "from pymoo.core.parameters import get_params, flatten, set_params, hierarchical\n",
    "\n",
    "algorithm = DE()\n",
    "flatten(get_params(algorithm))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If not provided directly, when initializing a `HyperparameterProblem` these variables are directly used for optimization."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Secondly, one needs to define what exactly should be optimized. For instance, for a single run on a problem (with a fixed random seed) using the well-known parameter optimization toolkit [Optuna](https://optuna.org), the implementation may look as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pymoo.algorithms.hyperparameters import SingleObjectiveSingleRun, HyperparameterProblem\n",
    "from pymoo.algorithms.soo.nonconvex.g3pcx import G3PCX\n",
    "from pymoo.algorithms.soo.nonconvex.optuna import Optuna\n",
    "from pymoo.core.parameters import set_params, hierarchical\n",
    "from pymoo.optimize import minimize\n",
    "from pymoo.problems.single import Sphere\n",
    "\n",
    "algorithm = G3PCX()\n",
    "\n",
    "problem = Sphere(n_var=10)\n",
    "n_evals = 500\n",
    "\n",
    "performance = SingleObjectiveSingleRun(problem, termination=(\"n_evals\", n_evals), seed=1)\n",
    "\n",
    "res = minimize(HyperparameterProblem(algorithm, performance),\n",
    "               Optuna(),\n",
    "               termination=('n_evals', 50),\n",
    "               seed=1,\n",
    "               verbose=False)\n",
    "\n",
    "hyperparams = res.X\n",
    "print(hyperparams)\n",
    "set_params(algorithm, hierarchical(hyperparams))\n",
    "\n",
    "res = minimize(Sphere(), algorithm, termination=(\"n_evals\", n_evals), seed=1)\n",
    "print(\"Best solution found: \\nX = %s\\nF = %s\" % (res.X, res.F))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Of course, you can also directly use the `MixedVariableGA` available in our framework:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pymoo.algorithms.hyperparameters import SingleObjectiveSingleRun, HyperparameterProblem\n",
    "from pymoo.algorithms.soo.nonconvex.g3pcx import G3PCX\n",
    "from pymoo.algorithms.soo.nonconvex.optuna import Optuna\n",
    "from pymoo.core.mixed import MixedVariableGA\n",
    "from pymoo.core.parameters import set_params, hierarchical\n",
    "from pymoo.optimize import minimize\n",
    "from pymoo.problems.single import Sphere\n",
    "\n",
    "\n",
    "algorithm = G3PCX()\n",
    "\n",
    "problem = Sphere(n_var=10)\n",
    "n_evals = 500\n",
    "\n",
    "performance = SingleObjectiveSingleRun(problem, termination=(\"n_evals\", n_evals), seed=1)\n",
    "\n",
    "res = minimize(HyperparameterProblem(algorithm, performance),\n",
    "               MixedVariableGA(pop_size=5),\n",
    "               termination=('n_evals', 50),\n",
    "               seed=1,\n",
    "               verbose=False)\n",
    "\n",
    "hyperparams = res.X\n",
    "print(hyperparams)\n",
    "set_params(algorithm, hierarchical(hyperparams))\n",
    "\n",
    "res = minimize(Sphere(), algorithm, termination=(\"n_evals\", n_evals), seed=1)\n",
    "print(\"Best solution found: \\nX = %s\\nF = %s\" % (res.X, res.F))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, optimizing the parameters for a **single random seed** is often not desirable. And this is precisely what makes hyper-parameter optimization computationally expensive. So instead of using just a single random seed, we can use the `MultiRun` performance assessment to average over multiple runs as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pymoo.algorithms.hyperparameters import HyperparameterProblem, MultiRun, stats_single_objective_mean\n",
    "from pymoo.algorithms.soo.nonconvex.g3pcx import G3PCX\n",
    "from pymoo.core.mixed import MixedVariableGA\n",
    "from pymoo.core.parameters import set_params, hierarchical\n",
    "from pymoo.optimize import minimize\n",
    "from pymoo.problems.single import Sphere\n",
    "\n",
    "\n",
    "algorithm = G3PCX()\n",
    "\n",
    "problem = Sphere(n_var=10)\n",
    "n_evals = 500\n",
    "seeds = [5, 50, 500]\n",
    "\n",
    "performance = MultiRun(problem, seeds=seeds, func_stats=stats_single_objective_mean, termination=(\"n_evals\", n_evals))\n",
    "\n",
    "res = minimize(HyperparameterProblem(algorithm, performance),\n",
    "               MixedVariableGA(pop_size=5),\n",
    "               termination=('n_evals', 50),\n",
    "               seed=1,\n",
    "               verbose=True)\n",
    "\n",
    "hyperparams = res.X\n",
    "print(hyperparams)\n",
    "set_params(algorithm, hierarchical(hyperparams))\n",
    "\n",
    "res = minimize(Sphere(), algorithm, termination=(\"n_evals\", n_evals), seed=5)\n",
    "print(\"Best solution found: \\nX = %s\\nF = %s\" % (res.X, res.F))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Another way of performance measure is the number of evaluations until a specific goal has been reached. For single-objective optimization, such a goal is most likely until a minimum function value has been found. Thus, for the termination, we use `MinimumFunctionValueTermination` with a value of `1e-5`. We run the method for each random seed until this value has been reached or at most `500` function evaluations have taken place. The performance is then measured by the average number of function evaluations (`func_stats=stats_avg_nevals`) to reach the goal."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pymoo.algorithms.hyperparameters import HyperparameterProblem, MultiRun, stats_avg_nevals\n",
    "from pymoo.algorithms.soo.nonconvex.g3pcx import G3PCX\n",
    "from pymoo.core.mixed import MixedVariableGA\n",
    "from pymoo.core.parameters import set_params, hierarchical\n",
    "from pymoo.core.termination import TerminateIfAny\n",
    "from pymoo.optimize import minimize\n",
    "from pymoo.problems.single import Sphere\n",
    "from pymoo.termination.fmin import MinimumFunctionValueTermination\n",
    "from pymoo.termination.max_eval import MaximumFunctionCallTermination\n",
    "\n",
    "algorithm = G3PCX()\n",
    "\n",
    "problem = Sphere(n_var=10)\n",
    "\n",
    "termination = TerminateIfAny(MinimumFunctionValueTermination(1e-5), MaximumFunctionCallTermination(500))\n",
    "\n",
    "performance = MultiRun(problem, seeds=[5, 50, 500], func_stats=stats_avg_nevals, termination=termination)\n",
    "\n",
    "res = minimize(HyperparameterProblem(algorithm, performance),\n",
    "               MixedVariableGA(pop_size=5),\n",
    "               ('n_evals', 50),\n",
    "               seed=1,\n",
    "               verbose=True)\n",
    "\n",
    "hyperparams = res.X\n",
    "print(hyperparams)\n",
    "set_params(algorithm, hierarchical(hyperparams))\n",
    "\n",
    "res = minimize(Sphere(), algorithm, termination=(\"n_evals\", res.f), seed=5)\n",
    "print(\"Best solution found: \\nX = %s\\nF = %s\" % (res.X, res.F))\n"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
